﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using KarlsTools;

namespace ImageProcessing
{
	public static class ConformalMappings
	{
		//args passed in double array
		// 

		public static double[, ,] Conformal1(double[, ,] adImage, int nImageWidth, int nImageHeight, double[] adParams, int nNumArgs)
		{
			if (nNumArgs <= 0)
				//error
				return null;
			double[,,] adOut;
			adOut = new double[nImageWidth,nImageHeight,3];
			double dMaxR = Math.Sqrt((double)nImageWidth * nImageWidth + (double)nImageHeight * nImageHeight) / 2;
			double dHalfWidth = (double)(nImageWidth / 2);
			double dHalfHeight = (double)(nImageHeight / 2);
			double dUserParam = adParams[0];
			double dx, dy;
			double r, theta;
			double du;
			double xp,yp;
			//Complex x,y,u,v;

			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{
					dx = (double)i - dHalfWidth;
					dy = (double)j - dHalfHeight;
					r = Math.Sqrt(dx * dx + dy * dy);
					theta = dUserParam * Math.Atan2(dy, dx);
					du = (Math.Pow((r / dHalfWidth),(double)dUserParam)) * dMaxR;

					xp = Math.Floor(du * Math.Cos(theta)) + dHalfWidth;
					yp = Math.Floor(du * Math.Sin(theta)) + dHalfHeight;

					if (xp < 0) xp = 0;
					if (xp >= nImageWidth) xp = nImageWidth -1;
					if (yp < 0) yp = 0;
					if (yp >= nImageHeight) yp = nImageHeight - 1;

					adOut[i, j, 0] = adImage[(int)xp, (int)yp, 0];
					adOut[i, j, 1] = adImage[(int)xp, (int)yp, 1];
					adOut[i, j, 2] = adImage[(int)xp, (int)yp, 2];

				} //end for
			} //end for

			return adOut;
		}//end Conformal1

		public static double[, ,] ConformalComplex1(double[, ,] adImage, int nImageWidth, int nImageHeight, double[] adParams, int nNumArgs)
		{
			//z = (w - (k + i)) / (w - 1)

			double[, ,] adOut = new double[nImageWidth, nImageHeight, 3];
			double[,,] adRaw = new double[nImageWidth,nImageHeight,2];
			double dHalfWidth = Math.Floor((double)nImageWidth / 2);
			double dHalfHeight = Math.Floor((double)nImageHeight / 2);
			double dX, dY;
			double dP, dQ;
			int nX,nY;
			
			Complex compW,compZ,compK,compI;
			compI = new Complex(0,1);

			double dMinX = double.MaxValue;
			double dMinY = double.MaxValue;
			double dMaxX = double.MinValue;
			double dMaxY = double.MinValue;
			
			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{

					dX = ((i - dHalfWidth) / nImageWidth);
					dY = ((j - dHalfHeight) / nImageHeight);

					compW = new Complex(dX,dY);
					compK = new Complex(adParams[0], adParams[1]);
					
					//the transformation
					compZ = (compW - (compK + compI)) / (compW - 1.0);
					
					dP = compZ.Real() * nImageWidth + dHalfWidth;
					dQ = compZ.Imag() * nImageHeight + dHalfHeight;

					if (dP > dMaxX) dMaxX = dP;
					if (dP < dMinX) dMinX = dP;
					if (dQ > dMaxY) dMaxY = dQ;
					if (dQ < dMinY) dMinY = dQ;

					adRaw[i, j, 0] = dP;
					adRaw[i, j, 1] = dQ;					
				}
			}

			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{
					dX = Math.Floor((adRaw[i, j, 0] - dMinX) / (dMaxX- dMinX) * nImageWidth);
					dY = Math.Floor((adRaw[i, j, 1] - dMinY) / (dMaxY- dMinY) * nImageHeight);

					if (dX > 0 && dX < nImageWidth && dY > 0 && dY < nImageHeight)
					{
						adOut[i, j, 0] = adImage[(int)dX, (int)dY, 0];
						adOut[i, j, 1] = adImage[(int)dX, (int)dY, 1];
						adOut[i, j, 2] = adImage[(int)dX, (int)dY, 2];
					}//end if
					else
					{
						adOut[i, j, 0] = 0;
						adOut[i, j, 1] = 0;
						adOut[i, j, 2] = 0;
					} //end else

				} //end for
			} //end for

			return adOut;
		}//end ConformalComplex1

		public static double[, ,] ConformalComplex2(double[, ,] adImage, int nImageWidth, int nImageHeight, double[] adParams, int nNumArgs)
		{
			//((1+3i)*w - 1)./(w + 3).^k

			double[, ,] adOut = new double[nImageWidth, nImageHeight, 3];
			double[, ,] adRaw = new double[nImageWidth, nImageHeight, 2];
			double dHalfWidth = Math.Floor((double)nImageWidth / 2);
			double dHalfHeight = Math.Floor((double)nImageHeight / 2);
			double dX, dY;
			double dP, dQ;
			int nX, nY;

			Complex compW, compZ, compK, compI,compY;
			compI = new Complex(0, 1);

			double dMinX = double.MaxValue;
			double dMinY = double.MaxValue;
			double dMaxX = double.MinValue;
			double dMaxY = double.MinValue;

			compK = new Complex(adParams[0], adParams[1]);
			compY = new Complex(1, 3.0);

			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{

					dX = ((i - dHalfWidth) / nImageWidth);
					dY = ((j - dHalfHeight) / nImageHeight);

					compW = new Complex(dX, dY);
					

					//the transformation
					compZ = Complex.Pow((compY * compW - 1.0)/(compW + 3.0),compK);

					dP = compZ.Real() * nImageWidth + dHalfWidth;
					dQ = compZ.Imag() * nImageHeight + dHalfHeight;

					if (dP > dMaxX) dMaxX = dP;
					if (dP < dMinX) dMinX = dP;
					if (dQ > dMaxY) dMaxY = dQ;
					if (dQ < dMinY) dMinY = dQ;

					adRaw[i, j, 0] = dP;
					adRaw[i, j, 1] = dQ;
				}
			}

			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{
					dX = Math.Floor((adRaw[i, j, 0] - dMinX) / (dMaxX - dMinX) * nImageWidth);
					dY = Math.Floor((adRaw[i, j, 1] - dMinY) / (dMaxY - dMinY) * nImageHeight);

					if (dX > 0 && dX < nImageWidth && dY > 0 && dY < nImageHeight)
					{
						adOut[i, j, 0] = adImage[(int)dX, (int)dY, 0];
						adOut[i, j, 1] = adImage[(int)dX, (int)dY, 1];
						adOut[i, j, 2] = adImage[(int)dX, (int)dY, 2];
					}//end if
					else
					{
						adOut[i, j, 0] = 0;
						adOut[i, j, 1] = 0;
						adOut[i, j, 2] = 0;
					} //end else

				} //end for
			} //end for

			return adOut;
		}//end ConformalComplex2

		public static double[, ,] ConformalComplex3(double[, ,] adImage, int nImageWidth, int nImageHeight, double[] adParams, int nNumArgs)
		{
			//(w -1).^2*acos(w)

			double[, ,] adOut = new double[nImageWidth, nImageHeight, 3];
			double[, ,] adRaw = new double[nImageWidth, nImageHeight, 2];
			double dHalfWidth = Math.Floor((double)nImageWidth / 2);
			double dHalfHeight = Math.Floor((double)nImageHeight / 2);
			double dX, dY;
			double dP, dQ;

			Complex compW, compZ, compK, compI;
			compI = new Complex(0, 1);

			double dMinX = double.MaxValue;
			double dMinY = double.MaxValue;
			double dMaxX = double.MinValue;
			double dMaxY = double.MinValue;

			compK = new Complex(adParams[0], adParams[1]);

			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{

					dX = ((i - dHalfWidth) / nImageWidth);
					dY = ((j - dHalfHeight) / nImageHeight);

					compW = new Complex(dX, dY);

					//the transformation
					compZ = (Complex.Pow(compW - 1.0, 2*Complex.Cosh(compW)))*adParams[0];

					dP = compZ.Real() * nImageWidth + dHalfWidth;
					dQ = compZ.Imag() * nImageHeight + dHalfHeight;

					if (dP > dMaxX) dMaxX = dP;
					if (dP < dMinX) dMinX = dP;
					if (dQ > dMaxY) dMaxY = dQ;
					if (dQ < dMinY) dMinY = dQ;

					adRaw[i, j, 0] = dP;
					adRaw[i, j, 1] = dQ;
				}
			}

			for (int j = 0; j < nImageHeight; j++)
			{
				for (int i = 0; i < nImageWidth; i++)
				{
					dX = Math.Floor((adRaw[i, j, 0] - dMinX) / (dMaxX - dMinX) * nImageWidth);
					dY = Math.Floor((adRaw[i, j, 1] - dMinY) / (dMaxY - dMinY) * nImageHeight);

					if (dX > 0 && dX < nImageWidth && dY > 0 && dY < nImageHeight)
					{
						adOut[i, j, 0] = adImage[(int)dX, (int)dY, 0];
						adOut[i, j, 1] = adImage[(int)dX, (int)dY, 1];
						adOut[i, j, 2] = adImage[(int)dX, (int)dY, 2];
					}//end if
					else
					{
						adOut[i, j, 0] = 0;
						adOut[i, j, 1] = 0;
						adOut[i, j, 2] = 0;
					} //end else

				} //end for
			} //end for

			return adOut;
		}//end ConformalComplex3
	}
}
